开一个新坑,Scala这门语言在优化上有很大的操作余地,需要相当的熟练度。本文仅做基础笔记的整理。
Scala
Scala 介绍
Scala 是 Scalable Language 的简写,是一门多范式的编程语言
联邦理工学院洛桑(EPFL)的Martin Odersky于2001年基于Funnel的工作开始设计Scala。
Funnel是把函数式编程思想和Petri网相结合的一种编程语言。
Odersky先前的工作是Generic Java和javac(Sun Java编译器)。Java平台的Scala于2003年底/2004年初发布。.NET平台的Scala发布于2004年6月。该语言第二个版本,v2.0,发布于2006年3月。
截至2009年9月,最新版本是版本2.7.6 。Scala 2.8预计的特性包括重写的Scala类库(Scala collections library)、方法的命名参数和默认参数、包对象(package object),以及Continuation。
2009年4月,Twitter宣布他们已经把大部分后端程序从Ruby迁移到Scala,其余部分也打算要迁移。此外, Wattzon已经公开宣称,其整个平台都已经是基于Scala基础设施编写的。
环境部分:
安装:和Java一样也要配置环境变量
配置IDEA:
先安装插件Scala
然后创建Maven项目
因为Maven默认不支持Scala
创建完毕之后
Scala文件夹标记为Source
语法部分
Hello Scala
1 | object HelloScala { |
命令台执行命令:
1 | scala -cp C:\Users\61661\Desktop\scala-1.0-SNAPSHOT.jar HelloScala |
声明值和变量
Scala声明变量有两种方式:val
和 var
val
定义的值是不可变的,它不是一个常量,是不可变量,或者称之为只读变量。
Tips:
- Scala的匿名变量(为了运行程序,系统自动添加的变量)分配
val
。 val
定义的变量虽然不能改变其引用的内存地址,但是可以改变其引用的对象的内部的其他属性值。- 为了减少可变性引起的bug,应该尽可能地使用不可变变量。变量类型可以省略,解析器会根据值进行推断。
val
和var
声明变量时都必须初始化。
常用类型
8种常用类型
类型 | 属性 |
---|---|
Boolean | true 或者 false |
Byte | 8位, 有符号 |
Short | 16位, 有符号 |
Int | 32位, 有符号 |
Long | 64位, 有符号 |
Char | 16位, 无符号 |
Float | 32位, 单精度浮点数 |
Double | 64位, 双精度浮点数 |
String | 由Char数组组成 |
与Java中的数据类型不同,Scala并不区分基本类型和引用类型,所以这些类型都是对象
可以调用相对应的方法,String直接使用的是java.lang.String
由于String实际是一系列Char的不可变的集合,Scala中大部分针对集合的操作,都可以用于String,具体来说,String的这些方法存在于类scala.collection.immutable.StringOps
中。
由于String在需要时能隐式转换为StringOps
,因此不需要任何额外的转换,String就可以使用这些方法。
每一种数据类型都有对应的Rich*
类型,如RichInt
、RichChar
等,为基本类型提供了更多的有用操作。
常用类型结构图
Scala中,所有的值都是类对象,而所有的类,包括值类型,都最终继承自一个统一的根类型Any
。统一类型,是Scala的又一大特点。更特别的是,Scala中还定义了几个底层类Bottom Class
,比如Null
和Nothing
。
Null
是所有引用类型的子类型,而Nothing
是所有类型的子类型。Null
类只有一个实例对象,null
,类似于Java中的null
引用。null
可以赋值给任意引用类型,但是不能赋值给值类型。Nothing
,可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于Nothing
是其他任意类型的子类,他还能跟要求返回值的方法兼容。Unit
类型用来标识过程,也就是没有明确返回值的函数。 由此可见,Unit
类似于Java
里的void
。Unit
只有一个实例,(),这个实例也没有实质的意义。
关系图:
算数操作符重载
+
-
*
/
%
可以完成和Java中相同的工作,但是有一点区别,他们都是方法。你几乎可以用任何符号来为方法命名。
1 + 2
等同于 1.+(2)
Tips: Scala中没有++、–操作符,需要通过+=、-=来实现同样的效果。
调用函数与方法
在Scala中,一般情况下我们不会刻意的去区分函数
与方法
的区别,但是他们确实是不同的东西。
后面我们再详细探讨。首先我们要学会使用Scala来调用函数与方法。
1.调用函数,求方根
1 | import scala.math |
2.调用方法,静态方法(Scala中没有静态方法这个概念,需要通过伴生类对象来实现)
1 | BigInt.probablePrime(16, scala.util.Random) |
3.调用方法,非静态方法,使用对象调用
1 | "HelloWorld".distinct |
4.apply与update方法
apply方法是调用时可以省略方法名的方法。用于构造和获取元素:
1 | "Hello"(4) 等同于 "Hello".apply(4) |
在StringOps
中你会发现一个 def apply(n: Int): Char
方法定义。update
方法也是调用时可以省略方法名的方法,用于元素的更新:
1 | arr(4) = 5 等同于 arr.update(4,5) |
Option类型
Scala为单个值提供了对象的包装器,表示为那种可能存在也可能不存在的值。他只有两个有效的子类对象,一个是Some,表示某个值,另外一个是None,表示为空,通过Option的使用,避免了使用null、空字符串等方式来表示缺少某个值的做法。
如:
1 | val map1 = Map("Alice" -> 20, "Bob" -> 30) |
控制结构和函数
if else
Scala中没有三目运算符,因为根本不需要。Scala中if else表达式是有返回值的,如果if或者else返回的类型不一样,就返回Any类型(所有类型的公共超类型)。
1 | object Demo { |
如果缺少一个判断,什么都没有返回,但是Scala认为任何表达式都会有值,对于空值,使用Unit类,写做(),叫做无用占位符,相当于Java中的void。
Tips: 行尾的位置不需要分号,只要能够从上下文判断出语句的终止即可。但是如果在单行中写多个语句,则需要分号分割。在Scala中,{}块包含一系列表达式,其结果也是一个表达式。块中最后一个表达式的值就是块的值。
while 表达式
Scala提供和Java一样的while和do循环,与If语句不同,While语句本身没有值,即整个While语句的结果是Unit类型的()。
1 | object Demo { |
while循环的中断
1 | import scala.util.control.Breaks |
Tips: Scala并没有提供break和continue语句来退出循环,如果需要break,可以通过几种方法来做1、使用Boolean型的控制变量 2、使用嵌套函数,从函数中return 3、使用Breaks对象的break方法。
for表达式
Scala也为for循环这一常见的控制结构提供了非常多的特性,这些for循环特性被称为for推导式(for comprehension)或for表达式(for expression).
for示例1: to左右两边为前闭后闭的访问
1 | for(i <- 1 to 3; j <- 1 to 3){ |
for示例2:until左右两边为前闭后开的访问
1 | for(i <- 1 until 3; j <- 1 until 3) { |
for示例3:引入保护式(也称条件判断式)该语句只打印1 3。保护式满足为true则进入循环内部,满足为false则跳过,类似于continue
1 | for(i <- 1 to 3 if i != 2) { |
for示例4:引入变量
1 | for(i <- 1 to 3; j = 4 - i) { |
for示例5:将遍历过程中处理的结果返回到一个,使用yield关键字
1 | val for5 = for(i <- 1 to 10) yield i |
for示例6:使用花括号{}代替小括号()
1 | for{ |
Tips: {}和()对于for表达式来说都可以。for 推导式有一个不成文的约定:当for
推导式仅包含单一表达式时使用原括号,当其包含多个表达式时使用大括号。值得注意的是,使用原括号时,早前版本的Scala 要求表达式之间必须使用分号。
函数
scala定义函数的标准格式为:
1 | def 函数名(参数名1: 参数类型1, 参数名2: 参数类型2) : 返回类型 = {函数体} |
函数示例1:返回Unit类型的函数:
1 | def shout1(content: String) : Unit = { |
函数示例2:返回Unit类型的函数,但是没有显式指定返回类型。(当然也可以返回非Unit类型的值)
1 | def shout2(content: String) = { |
函数示例3:返回值类型有多种可能,此时也可以省略Unit
1 | def shout3(content: String) = { |
函数示例4:带有默认值参数的函数,调用该函数时,可以只给无默认值的参数传递值,也可以都传递,新值会覆盖默认值;传递参数时如果不按照定义顺序,则可以通过参数名来指定。
1 | def shout4(content: String, leg: Int = 4) = { |
函数示例5:变长参数(不确定个数参数,类似Java的…)
1 | def sum(args: Int*) = { |
递归函数:递归函数在使用时必须有明确的返回值类型
1 | def factorial(n: Int): Int = { |
Tips:
1、Scala可以通过=右边的表达式 推断出函数的返回类型。如果函数体需要多个表达式,可以用代码块{}。
2、可以把return 当做 函数版本的break语句。
3、递归函数一定要指定返回类型。
4、变长参数通过* 来指定,所有参数会转化为一个seq序列。
过程
我们将函数的返回类型为Unit的函数称之为过程。
定义过程示例1:
1 | def shout1(content: String) : Unit = { |
定义过程示例2:
1 | def shout1(content: String) = { |
定义过程示例3:
1 | def shout1(content: String) { |
尖叫提示:这只是一个逻辑上的细分,如果因为该概念导致了理解上的混淆,可以暂时直接跳过过程这样的描述。毕竟过程,在某种意义上也是函数。
懒值
当val被声明为lazy时,他的初始化将被推迟,直到我们首次对此取值,适用于初始化开销较大的场景。
lazy示例:通过lazy关键字的使用与否,来观察执行过程
1 | object Lazy { |
异常
当碰到异常情况时,方法抛出一个异常,终止方法本身的执行,异常传递到其调用者,调用者可以处理该异常,也可以升级到它的调用者。运行系统会一直这样升级异常,直到有调用者能处理它。 如果一直没有处理,则终止整个程序。
Scala的异常的工作机制和Java一样,但是Scala没有“checked”异常,你不需要声明说函数或者方法可能会抛出某种异常。受检异常在编译器被检查,java必须声明方法所会抛出的异常类型。
抛出异常:用throw关键字,抛出一个异常对象。所有异常都是Throwable的子类型。throw表达式是有类型的,就是Nothing,因为Nothing是所有类型的子类型,所以throw表达式可以用在需要类型的地方。
捕捉异常:在Scala里,借用了模式匹配的思想来做异常的匹配,因此,在catch的代码里,是一系列case字句。
异常捕捉的机制与其他语言中一样,如果有异常发生,catch字句是按次序捕捉的。因此,在catch字句中,越具体的异常越要靠前,越普遍的异常越靠后。 如果抛出的异常不在catch字句中,该异常则无法处理,会被升级到调用者处。
finally字句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作。
异常示例:
1 | object ExceptionSyllabus { |
数据结构
数据结构特点
Scala同时支持可变集合和不可变集合,不可变集合从不可变,可以安全的并发访问。
两个主要的包:
不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable
Scala优先采用不可变集合,对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本。
不可变集合继承层次:
可变集合继承层次:
数组Array
1.定长数组
1 | //定义 |
2.变长数组
1 | //定义 |
3.定长数据与变长数据的装换
1 | arr1.toBuffer |
4.多维数据
1 | //定义 |
5.与Java数组的互转
1 | //scala => Java |
6.数据的遍历
1 | for(x <- arr1) { |
5.3 元组 Tuple
元组可以理解为一个容器,可以存放各种相同或者不同类型的数据。
创建
1 | val tuple1 = (1, 2, 3, "heiheihei") |
访问(注意元素元素访问邮箱划线,并且访问下标从1开始,而不是0)
1 | val value1 = tuple1._4 |
元组的遍历
方式1
1 | for (elem <- tuple1.productIterator) { |
方式2
1 | tuple1.productIterator.foreach(i => println(i)) |
列表List
如果List列表为空,则使用Nil来表示
创建List
1 | val list1 = List(1, 2) |
访问List元素
1 | val value1 = list1(1) |
List元素的追加
1 | val list2 = list1 :+ 99 |
List的创建与追加,符号“::”,注意观察去掉Nil和不去掉Nil的区别
1 | val list4 = 1 :: 2 :: 3 :: list1 :: Nil |
队列Queue
队列数据存取符合先进先出的策略
队列的创建
1 | import scala.collection.mutable |
队列元素的追加
1 | q1+=1 |
队列的追加
1 | q1 ++= List(2, 3, 4) |
按照进入队列的顺序删除元素
1 | q1.dequeue() |